ML is fun!(Part 2)

英文出处:https://medium.com/@ageitgey/machine-learning-is-fun-part-2-a26a10b68df3#.8arwrabzl
此篇是系列文章machine learning is fun的第二部分,讲解了如何找到特征,用递归神经网络生成”超级玛丽游戏”和生成海明威风格的文本。

在第一章,我们说,机器学习是使用泛型算法来告诉你一些关于数据的有趣的事情,而不需要写任何代码具体到你正在解决的问题。(如果你还没有阅读过part 1,立马去看!)

这一次,我们将看到一种泛型算法去做一些非常酷的事情——创建视频游戏关卡,这看起来像是人类做的。我们将建立一个神经网络,用现有的超级马里奥关卡训练它,并且观察新的游戏关卡产生。

  • One of the levels our algorithm will generate

就像第一章,本指南是面向任何对机器学习充满好奇但又不知如何入手的人。目标是让更多的人收益,这意味着文章有很多的概括并且跳过了很多细节。但是谁在乎呢?如果这让人对机器学习更感兴趣了,文章目的也就达到了。

Making Smarter Guesses

回到第一章,我们根据房子的属性,创建了一个简单的算法用来估价一个房子的价值。给出一个房子的数据就像这样:

  • enter description here

我们完成了这个简单的估价函数:

1
2
3
4
5
6
7
8
9
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood):
price = 0
# a little pinch of this
price += num_of_bedrooms * 0.123
# and a big pinch of that
price += sqft * 0.41
# maybe a handful of this
price += neighborhood * 0.57
return price

换句话说,我们估计房子的价格是通过乘以该房子每一个属性的权值,然后我们把这些数字加起来,就得到了房子的价格。

我们用一个简单的图表来表达相同的功能,而不是用代码:

  • The arrows represent the weights in our function.

然而,该算法仅能解决简单的问题,比如结果与输入有线性关系的。如果房价背后的真想不是如此简单的?例如,街区对大户型和小户型房子影响很大,但是对中型房子就没那么重要。我们怎样才能在我们的模型中捕捉到那种复杂的细节呢?

为了更加聪明,我们可以用不同的权值运行这个算法多次,来捕捉不同的边界情况:

  • Let’s try solving the problem four different ways

现在我们有四组不同的估价。让我们把这4个估价合并成一个最终的估价。我们再次通过同样的算法运行它们(但是使用另外一组权值)!

  • enter description here

我们新的Super Answer融合了此前4个不同的估价。正因为如此,新的Answer比我们在单个简单的模型下捕获得更多。

What is a Neural Network?

让我们把4次尝试猜价合并到一张大图里面:

  • enter description here

这就是一个神经网络!每个节点都知道怎样在一组输入中,对它们赋予权重,并计算出一个输出值。把所有节点连接起来,我们就可以模拟复杂的功能。

为了本文的简介,我跳过了很多(包括特征提取和激活函数),但是最重要的部分是这些基础思想:

  • 我们做了一个简单的估价函数,以一组输入和乘以它们的权重得到一个输出。我们称这个简单函数为一个神经元。
  • 通过许多简单的神经元连接在一起,我们可以函数模型化,这些过于复杂的函数都是由单个神经元建模而来。

这像极了乐高!我们不能用单个乐高块来建立模型,但是如果我们用足够多的乐高块组合在一起,我们就可以建立任何东西的模型:

  • A grim preview of our plastic animal future? Only time can tell…

Give our Neural Network a Memory

当你给它相同的输入时,我们所看到的神经网络总是返回相同的答案。它没有记忆。从编程角度来看,神经网络是无状态算法。

在许多场景(例如估计房屋价格),这正式你想要的。但是这种模型不能做的一件事,是应对数据模式的变化随着时间的推移。

想像一下,我递给你一个键盘,并要你写一个故事。但是在你写之前,我的任务是猜测一下你最优可能敲下的第一个字母。那我应该猜哪个字母呢?

我可以运用我的英语知识来增加我猜测正确字母的几率。比如,你可能会输入一个单词开始时常见的字母。如果我看过你曾经写过的故事,那么基于你经常在故事开头使用的单词,我可以经一步缩小范围。一旦我拥有了全部数据,我可以建立一个神经网络模型来预测你即将开始的字母是什么。

我们的模型可能看起来像这样:

  • enter description here

让我们把问题升级的更难一点。我需要猜测下一个字母在你故事中任何地方。这是一个更有趣的问题。

让我们用Ernest Hemingway’s The Sun Also Rises 的前几个单词为例:

Robert Cohn was once middleweight boxi

下一个字母是什么呢?

你可能猜是’n’-这个单词可能是boxing.我们知道这是基于我们已经在句子中看到的单词和我们英语的先验知识。同时,单词’middleweight’给我们一个额外的线索——我们讨论的是拳击(boxing)。

要用神经网络来解决这个问题,我们需要给我们的模型添加状态。每一次我们要神经网络给出一个答案,我们也要保存中间计算结果,并重新使用它们作为下一次的部分输入。这样,我们的模型会根据最近看到的输入来调整其预测。

  • enter description here

跟踪我们模型中的状态,使得它可能不只是预测故事中的第一个字母,还在给定所有先前单词的情况下预测下一个字母。

这是递归神经网络的的基本思想。我们每次更新网络都是使用该思想。这允许它根据最近看到的来更新其预测。它甚至可以模式模型化随着时间的推移,只要我们给神经网络足够的内存。

What’s a single letter good for?

预测一则故事中下一个字母看起来毫无用处。这有什么意义呢?

一个很酷的应用是,可以自动预测的手机键盘:

  • The next most likely letter is “t”.

但是如果我们把这个想法趋向于极端呢?如果我们要求模型预测下一个最有可能出现的字符,一直一直这么预测下去——永远?那么它会为我们写下一个完成的故事!

Generating a story

我们看到我们如何猜到海明威句子的下一个单词。我们尝试生成一个完整的海明威风格的故事。

为了实现这个目标,我们使用递归神经网络,由Andrej Karpathy所写。Andrej是斯坦福大学的一名深度学习的研究者,他写了一篇非常优秀的介绍性论文,是关于用RNNs生成文本。你可以在github上查看所有该模型的代码。

我们将从The Sun Also Rises完整的书里面创建我们的模型,这本书——使用84个不同的字母,共362,239个字符(包括标点符号,大写/小写,等)。这个数据集实际上远小于真实世界的应用。要产生一个很好地、有海明威风格的模型,它最好有几倍于样本数量的文本。但是一本书已经足以发挥功效。

当我们开始训练RNN,它并不能很好地预测字母。以下就是它经过100个循环后的训练结果:

hjCTCnhoofeoxelif edElobe negnk e iohehasenoldndAmdaI ayio pe e h’e
btentmuhgehi bcgdltt. gey heho grpiahe. Ddelnss.eelaishaner” cot AAfhB
ht ltny ehbih a”on bhnte ectrsnae abeahngy amo k ns aeo?cdse nh a
taei.rairrhelardr er deffijha

你可以清楚地看到,它已经断定单词与单词之间是由空格的。

1000次迭代后,事情开起来更有前景了:

hing soor ither. And the caraos, and the crowebel for figttier and ale
the room of me? Streat was not to him Bill-stook of the momansbed mig
out ust on the bull, out here. I been soms inick stalling that aid.
“Hon’t me and acrained on .Hw’s don’t you for the roed,” In’s pair.”
“Alough marith him.”

该模型已经能在基本句子结构中识别模式。它能在句子的末尾加上句号,甚至加上了对话引号。有几句话是可以识别的,但是更多的句子是没有意义的。

但是经过几千次的迭代交互训练,该模型看起来相当完美:

He went over to the gate of the café. It was like a country bed. “Do
you know it’s been me.” “Damned us,” Bill said. “I was dangerous,” I
said. “You were she did it and think I would a fine cape you,” I said.
“I can’t look strange in the cab.” “You know I was this is though,”
Brett said. “It’s a fights no matter?” “It makes to do it.” “You
make it?” “Sit down,” I said. “I wish I wasn’t do a little with the
man.” “You found it.” “I don’t know.” “You see, I’m sorry of
chatches,” Bill said. “You think it’s a friend off back and make you
really drunk.”

在这一点上,该算法捕捉了海明威简短直接的对话写作的基本模式。有些句子甚至是有意义的。

与这本书中一些真实的句子做比较:

There were a few people inside at the bar, and outside, alone, sat
Harvey Stone. He had a pile of saucers in front of him, and he needed
a shave. “Sit down,” said Harvey, “I’ve been looking for you.”
“What’s the matter?” “Nothing. Just looking for you.” “Been out to
the races?” “No. Not since Sunday.” “What do you hear from the
States?” “Nothing. Absolutely nothing.” “What’s the matter?”

即使是只一次寻找一个字符的模式,我们的算法用适当的格式重现了看似合理的散文。这是多么的惊人!

我们不必从头开始生成文本。我们可以提供算法最开始的几个字母,并让它自己找到接下来的字母。

为了更有趣,我们用种子文本“Er”,”He”和“The S”来生成新的作者名字和新的标题,用于我们想象假书的封面:

  • The real book is on the left and our silly computer-generated nonsense book is on the right.

不错哦!

不过,真正令人兴奋的是该算法可以指出任何数据序列中的模式。它可以很容易的生成看似真实的菜谱或者假的奥巴马演讲稿。但是为什么要先知我们人类自己的语言呢?我们可以将相同的想法应用到任何一种有模式的数据序列中。

Making Mario without actually Making Mario

在2015年,Nintendo为Wii U游戏系统发布了超级马里奥制造器。

  • Every kid’s dream!

这款游戏可以让你在游戏手柄上画出自己的超级马里奥关卡,然后上传到网上,这样你的小伙伴们也可以一起玩。你可以在你自己的关卡中,设计原来马里奥游戏中所有经典的敌人形象。

我们可以使用生成假海明威文本的模型,同样去生成假的超级马里奥关卡吗?

首先,我们需要一个数据集来训练我们的模型。从1985年发布最初版本的超级马里奥游戏以来所有的版本关卡,都作为我们的训练数据:

  • Best Christmas Ever. Thanks Mom and Dad!

这个游戏有32个关卡,并且70%是相同的风格。所以我们会延续这样的风格。

为了得到每个关卡的设计风格,我拿到了马里奥游戏的一个原始副本,并写了一个程序从游戏的内存中拉取关卡设计。超级马里奥是一个有着30年历史的游戏,网上有很多的资源帮助你找出关卡是如何存储在游戏的内存中的。从老旧视频游戏中提取关卡数据是一个很有趣的编程练习,你应该在某个时候尝试一下。

这是超级马里奥的第一关(如果你曾玩过,你可能会记得):

  • Super Mario Bros. Level 1–1

如果我们仔细观察,我们可以看到关卡是由一个简单的网格对象构成的:

  • enter description here

我们可以很简单的用字符序列来表示这个网格,一个字符代表一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
--------------------------
--------------------------

--------------------------
#??#----------------------
--------------------------

--------------------------
--------------------------

-##------=--=----------==-
--------==--==--------===-

-------===--===------====-
------====--====----=====-

=========================-

我们在关卡中用一个字符替换一个对象:

  • ‘-‘表示空格
  • ‘=’表示实心的砖块
  • ‘#’表示易碎的砖块
  • ‘?’表示金砖

使用不同的字符代表关卡中不同种类的对象。

我完成的文本文件看起来像这样:

  • enter description here

观察文本文件,如果一行一行的读下去,你可以看到马里奥的关卡没有一个很明显的模式:

  • Reading line-by-line, there’s not really a pattern to capture. Lots of lines are completely blank.

模式会在一种层级上真正地出现,当你把这层级看成是一列一列的:

  • Looking column-by-column, there’s a real pattern. Each column ends in a ‘=’ for example.

所以为了算法能够在我的数据中找到通用的模式,我们需要以列的形式训练数据。找出你的输入数据中有效的表示方式(称之为特征选择)是使用机器学习算法的关键因素之一。

为了训练模型,我需要90度旋转我的文本文件。该行为确保了字符按顺序送入模型中,通用模式更容易出现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-----------=
-------#---=
-------#---=
-------?---=
-------#---=
-----------=

-----------=
----------@=
----------@=
-----------=

-----------=
-----------=

---------PP=
---------PP=
----------==

---------===
--------====

-------=====
------======

-----=======
---=========

---=========

Training Our Model

就像我们看到海明威散文模型被创造的时候一样,一个模型的改进正是因为我们训练它自身。

经过一丁点的训练后,我们的模型一直产生垃圾:

1
2
3
4
5
6
7
8
9
10
11
--------------------------
LL+<&=------P-------------
--------

---------------------T--#--
-----

-=--=-=------------=-&--T--------------
--------------------

--=------$-=#-=-_
--------------=----=<----
-------b
-

它表现出来一个想法,’-‘和’=’应该有很多。但是它还没有指出模式。

经过几千次的迭代,它开始看起来想点什么了:

1
2
3
4
5
6
7
8
9
10
11
--
-----------=

----------=
--------PP=
--------PP=
-----------=

-----------=
-----------=

-------?---=
-----------=

-----------=

该模型几乎可以计算出每一行应该有相同的长度。它似乎已经开始计算出马里奥的一些逻辑:管道有两个块的宽度,高度至少两个块,所以”P”应该以2x2的矩阵块方式出现。多么酷的事情!

随着更多次的训练,该模型可以在任意点上生成最有效的数据:

1
2
3
4
5
6
7
8
--------PP=
--------PP=
----------=

----------=
----------=

---PPP=---=
---PPP=---=
----------=

从我们的模型中截取一段完整的数据,并旋转其到水平位置:

  • A whole level, generated from our model!

这个数据看起来很不错!有几个很惊人的事情可以注意到:

  • 它把Lakitu从关卡开始就放到了天空上——这一点像极了真正的马里奥游戏关卡。
  • 它知道漂浮在空气中的管道应该扎根在砖块上,而不是停留在空气中。
  • 它把敌人放到了合乎逻辑的地方。
  • 它没有创造出任何阻碍玩家前进的东西。

最后,我们采用这个关卡,并在超级马里奥制造器里面重新创建该游戏:

  • Our level data after being entered into Super Mario Maker

%enter description here

Toys vs. Real World Applications

我们使用的递归神经网络算法来训练我们的模型,现实世界的公司来解决困难的问题,比如语音检测和语义识别。什么原因使得我们的模型像一个“玩具”而不是前沿科技,是因为我们的模型是由非常小的数据生成。原来的超级马里奥游戏里没有提供足够多的数据用于生成一个好的模型。

如果我们能够获得成千上万的用户创建的超级马里奥游戏关卡,那我们就能做出一个惊人的模型。但是我们不能——Nintendo不会让我们这么干。大公司不会免费开放他们的数据。

随着机器学习在更多的行业变得越来越重要,好程序与坏程序之间的区别将是有多少数据你可以用于训练你的模型。这就是为什么像Google和Facebook这样的公司极需要你的数据!

例如,Google最近的开源项目TensorFlow,这是建设大规模机器学习应用的软件开发包。Google免费开放了如此重要的技术,这是一个相当大的交易,可以壮大Google Translate。
Google Translate。
但是,没有Google对每一种语言巨大的数据宝库,你也不能创造一个竞争者对抗Google Translate。数据给了Google前沿的竞争力。试着想一下,你下一次打开Google地图的历史位置或者Facebook的历史位置,可以注意到它记录了你曾经去过的每一个地方。

Further Reading

在机器学习中,从来没有单独一个方法解决问题。当决定如何预处理你的数据、选择何种算法的时候,你又无限的选择。经常地融合多种方法会给你更佳的结果,比任何单一的方法。

热评文章